﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using com.bodurov.SessionRecovery.Interfaces;

namespace com.bodurov.SessionRecovery
{
    public class SessionRecoveryLogger : ISessionRecoveryLogger
    {
        private readonly ISessionRecoveryConfiguration _config;
        private string _latestLogName;

        private static readonly object _synchRoot1 = new object();
        private static readonly object _synchRoot2 = new object();
        private static readonly object _synchRoot3 = new object();

        public SessionRecoveryLogger(ISessionRecoveryConfiguration config)
        {
            this._config = config;
        }

        public bool LogIsStillOpen
        {
            get { return File.Exists(this._config.LogFileName); }
        }

        public void ManagerIsEnabled()
        {
            this.CreateNewLogFile();
        }

        public void Log(TraceLevel level, string message, params object[] args)
        {
            if(level > this._config.TraceLevel)
            {
                return;
            }
            lock(_synchRoot2)
            {
                var logFileName = File.Exists(this._config.LogFileName)
                                    ? this._config.LogFileName
                                    : this.GetLatestLog();
                if(logFileName == null)
                {
                    this.CreateNewLogFile();
                    logFileName = this._config.LogFileName;
                }
                File.AppendAllText(logFileName, FormatLogMessage(level, String.Format(message, args)));
            }
        }

        public void ManagerIsDisabled()
        {
            this.BackupLogFile();
            this.DeleteOlderLogs();
        }

        private void DeleteOlderLogs()
        {
            var dir = new DirectoryInfo(this._config.SessionFilesFolder);
            var logFiles = dir.GetFiles("Log_*", SearchOption.TopDirectoryOnly);
            for (var i = 0; i < (logFiles.Length - this._config.MaxNumberLogsToKeep); i++)
            {
                logFiles[i].Delete();
            }
        }

        private string GetLatestLog()
        {
            if(this._latestLogName == null)
            {
                var dir = new DirectoryInfo(this._config.SessionFilesFolder);
                var logFiles = dir.GetFiles("Log_*", SearchOption.TopDirectoryOnly);
                if (logFiles.Length > 0)
                {
                    this._latestLogName = logFiles[logFiles.Length - 1].FullName;
                }
            }
            return this._latestLogName;
        }

        private void CreateNewLogFile()
        {
            lock (_synchRoot1)
            {
                if (!File.Exists(this._config.LogFileName))
                {
                    using(var stream = File.CreateText(this._config.LogFileName))
                    {
                        stream.Close();
                    }
                }
            }
        }

        private void BackupLogFile()
        {
            lock (_synchRoot3)
            {
                if (File.Exists(this._config.LogFileName))
                {
                    this._latestLogName = GetBackedUpLogFileName();
                    File.Move(this._config.LogFileName, this._latestLogName);
                }
            }
        }

        private string GetBackedUpLogFileName()
        {
            var justFileName = Path.GetFileName(this._config.LogFileName);
            return Path.Combine(
                        this._config.LogFileName.Substring(0, this._config.LogFileName.Length - justFileName.Length), 
                        "Log_"+this._config.DateToString(DateTime.UtcNow)+"_"+(new Random()).Next(1000,9999)+".log"
                    );
        }

        private static string FormatLogMessage(TraceLevel level, string str)
        {
            return String.Format("-----------------------\r\n{0} ({1}): {2}\r\n", DateTime.UtcNow, level, str);
        }
    }
}
